import socket
import sys
import Queue
from threading import Thread
import time
import select

import ttrConst 
reload(ttrConst)
from ttrConst import *

import ttrWidget
reload(ttrWidget)
from ttrWidget import msg_table

import ttrClient
reload(ttrClient)

cliet_status = {'connected':0, 'new':1, 'checking':2, 'waiting':3, 'running':4, 'stop':5, 'err':-1}

class client:
    def __init__(self, connection, addr, in_q, out_q):
        self.conn = connection
        self.addr = addr
        self.info = {} #configuration of client, name, etc
        self.timeout = time.time() + HEART_BEAT_TIME
        
        self.thread = None
        self.fromClient_q = in_q # from client
        self.toClient_q = out_q # to client
        self.uncompleted = '' # message buffer
        self.buf = '' # TCP buffer
        self.status = cliet_status['connected']
        self.run()
        
    def run(self):
        self.conn.setblocking(0)
        self.thread = Thread(target=self.MonitorClient_thread) #, args=(i,))
        self.thread.setDaemon(True) # 
        self.thread.start()
        
    def MonitorClient_thread(self):
        print 'mornitoring: ', self.addr
        while self.status != cliet_status['stop']:
            while not self.toClient_q.empty():
                self.TCP_send(self.toClient_q.get()) #pass to client
            else:
                self.chkHeartbeat()
                    
            q_msg = self.TCP_recv()
            for m in q_msg:
                self.fromClient_q.put(m) #pass to centre
            time.sleep(SERVER_CLIENT_SLEEP_TIME)
        else:
            #print 'connection closed on: ', self.addr
            self.conn.close()
            
    def TCP_send(self, msg):
        msg += TERMINATOR
        try:
            self.conn.send(fmtX %len(msg) + msg)
        except:
            self.status = cliet_status['stop']

    def TCP_recv(self):
        q_msg = []
        ready = select.select([self.conn], [], [], 0.1)
        if ready[0]:
            try:
                self.buf += self.conn.recv(10)
            except:
                # connection closed
                self.status = cliet_status['stop']
                
            if len(self.buf)>HEAD_NUMBER_SIZE and len(self.buf[HEAD_NUMBER_SIZE:]) >= int(self.buf[:HEAD_NUMBER_SIZE],16):
                # completion data package, maybe more
                # print 'msg:(%d)%s' %(int(self.buf[:HEAD_NUMBER_SIZE], 16), self.buf[HEAD_NUMBER_SIZE:])
                data = self.buf
                q_msg = data.split(TERMINATOR)
                #print q_msg
                self.buf = q_msg[-1]
                
        return [m[HEAD_NUMBER_SIZE:] for m in q_msg[:-1]]

    def stop(self):
        self.status = cliet_status['stop']
        
    def get_status(self):
        return self.status
        
    def set_status(self, status):
        self.status = status
        
    def chkHeartbeat(self):
        if time.time() > self.timeout:
            #send heartbeat
            self.TCP_send(cmd_table['heart'])
            self.timeout = time.time() + HEART_BEAT_TIME
        
def LaunchLocalClient():
    def localClient_thread():
        # Launch local client
        local = socket.gethostbyname_ex(socket.gethostname())[0]
        ttrClient.main(local)

    time.sleep(CLIENT_WAIT_FOR_SERVER)
    LocalClient = Thread(target=localClient_thread) #, args=(i,))
    LocalClient.setDaemon(True) # 
    LocalClient.start()


        
# -------- main processing         
def clientProcess(ClientGroup, master):
    def proc_info(msg):
        if MAGIC_WORD in msg: master.kill_main() # stop all
        # broadcast
        for each in ClientGroup:
        # send msg to all clients
            each.toClient_q.put(msg)
            
    def proc_cmd(c, msg):
        print 'command from (%s,%d): %s' %(c.addr[0], c.addr[1], msg)
        
    def proc_conf(c, msg):
        # update 2s before confirm
        # configuration information
        c.info.update(eval(msg[MSG_HEAD:]))
        print 'config from (%s,%d): ' %(c.addr[0], c.addr[1]), c.info
        # now the client join the game
        #c.set_status(cliet_status['new'])

    def proc_unknown(c, msg):
        print 'unknown from (%s,%d): %s' %(c.addr[0], c.addr[1], msg)
        # return the message
        c.toClient_q.put(msg)
        
    def proc_remove(c):
        ClientGroup.remove(c)
        print '(%s,%d) is off, current link number:%d' %(c.addr[0], c.addr[1], len(ClientGroup))
        
    def proc_seating(c, seat):
        c.set_status(cliet_status['checking'])
        #exchange info with new comer
        c.info[TABLE_SEAT_FIELD] = TABLE_SEAT[seat]
        c.toClient_q.put(cmd_table['conf'] + str(c.info))
        
    def proc_welcome(c):
        for other in ClientGroup:
            if other != c:
                other.toClient_q.put(cmd_table['conf'] + str(c.info))
                c.toClient_q.put(cmd_table['conf'] + str(other.info))
                
    def proc_stop_all():
        for c in ClientGroup:
            c.stop()
        print 'clientProcess stop'
        
    
    print 'clientProcessing'
    # LaunchLocalClient()
    seat = 0
    # --------- main proc loop ------------
    while master.main_state == server_main_state['OK']:
        
        for c in ClientGroup:
            if not c.fromClient_q.empty():
                msg = c.fromClient_q.get().strip()
                if msg[:MSG_HEAD] == cmd_table['seat']: 
                    proc_seating(c, seat) # the first command
                    seat += 1; seat %= len(TABLE_SEAT) # next seat
                    
                elif msg[:MSG_HEAD] == cmd_table['conf']: proc_conf(c, msg)
                elif msg[:MSG_HEAD] == cmd_table['info']:  proc_info(msg)
                elif msg[:MSG_HEAD] == cmd_table['cmd']: proc_cmd(c, msg)
                elif msg[:MSG_HEAD] == cmd_table['heart']: pass
                else: proc_unknown(c, msg)
                    
            if c.status == cliet_status['stop']: proc_remove(c)
            elif c.status == cliet_status['new']: proc_welcome(c)
                        
        time.sleep(SERVER_CLIENT_PROC_SLEEP)        
    else:
        proc_stop_all()
        